package com.heisen.redis.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;

import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 *
 */
public class RedisUtils {

    private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Value("${spring.application.name}")
    private String appName;

    @Value("${spring.application.item-name:heisen}")
    private String xmName;


    private String prefixKey(){
        return xmName+":"+appName+":";
    }

    /**
     * 获取锁的最终key
     * @param key
     * @return
     */
    private String getXmKey(String key){
        return prefixKey() + key;
    }


    /**
     * 发布订阅
     * @param chanel
     * @param obj
     */
    public void  convertAndSend(String chanel,Object obj){

        redisTemplate.convertAndSend(chanel,obj);
    }

    /**
     * 根据key 从redis 数据
     * @param key
     * @return
     */
    public Object getForKey(String key){
        Object obj=redisTemplate.opsForValue().get(getXmKey(key));
        if(obj!=null){
            return obj;
        }
        return null;
    }


    /**
     * 验证key 的存在性
     * @param key
     * @return
     */
    public boolean hasKey(String key){
        return  redisTemplate.hasKey(getXmKey(key));
    }


    /**
     * 验证key 是否是项目的key
     * @param key
     * @return
     */
    public boolean startsWithXmkey(String key){
        return  key.startsWith(prefixKey());
    }


    /**
     * 设置对象
     * @param key
     * @param obj
     * @return
     */
    public boolean setForObj(String key,Object obj){
        return setForObj(key,obj,1800);
    }

    public boolean setForObjT(String key,Object obj){
        try{
            redisTemplate.opsForValue().set(getXmKey(key),obj);
            return true;
        }catch (Exception e){
            return false;
        }

    }

    public boolean setForObj(String key,Object obj,long time){
        try {
            redisTemplate.opsForValue().set(getXmKey(key), obj,time,TimeUnit.SECONDS);
            return true;
        }catch(Exception e){
            e.printStackTrace();
            logger.error(e.getStackTrace().toString());
            return false;
        }
    }

    public boolean setForObj(String key,Object obj,long time,TimeUnit timeUnit){
        try {
            redisTemplate.opsForValue().set(getXmKey(key), obj,time,timeUnit);
            return true;
        }catch(Exception e){
            e.printStackTrace();
            logger.error(e.getStackTrace().toString());
            return false;
        }
    }


    /**
     * 永久
     * @param key
     * @param obj
     * @return
     */
    public boolean setForObjL(String key,Object obj){
        try {
            redisTemplate.opsForValue().set(getXmKey(key), obj);
            return true;
        }catch(Exception e){
            e.printStackTrace();
            logger.error(e.getStackTrace().toString());
            return false;
        }
    }

    public boolean delForKey(String key){
        try {
            redisTemplate.delete(getXmKey(key));
            return true;
        }catch(Exception e){
            logger.error(e.getStackTrace().toString());
            return false;
        }
    }


    /**
     * 5秒限制
     * @param key
     * @param obj
     * @return
     */
    public boolean setForIp(String key,Object obj){
        try {
            redisTemplate.opsForValue().set(getXmKey(key), obj,2,TimeUnit.SECONDS);
            return true;
        }catch(Exception e){
            logger.error(e.getStackTrace().toString());
            return false;
        }
    }

    /**
     * 锁住
     * @param key
     * @param value
     * @param time
     * @return
     */
    public boolean lock(String key,String value,long time){
        Boolean bol=redisTemplate.opsForValue().setIfAbsent(getXmKey(key),value,time,TimeUnit.SECONDS);
        return bol!=null&&bol;
    }

    /**
     * 删除锁
     * @param key
     */
    public void releaseLock(String key){
        redisTemplate.delete(getXmKey(key));
    }

    /**
     * 获取redis锁
     * @param key 锁键
     * @param expire 过期时间
     */
    public boolean getLock(String key, long expire, TimeUnit timeUnit){
        // 获取本地ip地址作为锁的value
        String value = getLocalIp();
        //获取最终key
        key = getXmKey(key);
        /*
         * 注：spring data redis 2.1 版本
         * 这里的setIfAbsent 等同于 redis的setnx和expire命令
         * 如果当前key不存在则返回true
         */
        Boolean lockFlag = redisTemplate.opsForValue().setIfAbsent(key, value,expire, timeUnit);
        //获取锁失败,已有其他服务占用了锁
        if(!lockFlag){
            value = (String)redisTemplate.opsForValue().get(key);
        }
        return lockFlag;
    }

    /**
     * 释放锁
     * @param key 锁键
     */
    public void unLock(String key){
        //获取最终key
        key = getXmKey(key);
        //获取锁的value
        String value = (String)redisTemplate.opsForValue().get(key);
        //先判断该锁是不是本机的锁,防止此操作将其他机器的锁释放导致无锁的情况
        if(getLocalIp().equals(value)){
            redisTemplate.delete(key);
        }
    }

    /**
     * 获取本地IP
     * @return
     */
    private String getLocalIp(){
        InetAddress inetAddress = null;
        try {
            inetAddress = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return inetAddress.getHostAddress();
    }





    //===============================list=================================

    /**
     * 获取  list 所有的数据
     * @param key
     * @return
     */
    public List<Object> lGetAll(String key){
        try {
            return lGet(key, 0, -1);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }




    /**
     * 获取list缓存的内容
     * @param key 键
     * @param start 开始
     * @param end 结束  0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end){
        try {
            return redisTemplate.opsForList().range(getXmKey(key), start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     * @param key 键
     * @return
     */
    public long lGetListSize(String key){
        try {
            return redisTemplate.opsForList().size(getXmKey(key));
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     * @param key 键
     * @param index 索引  index>=0时， 0 表头，1 第二个元素，依次类推；index<0时，-1，表尾，-2倒数第二个元素，依次类推
     * @return
     */
    public Object lGetIndex(String key,long index){
        try {
            return redisTemplate.opsForList().index(getXmKey(key), index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(getXmKey(key), value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(getXmKey(key), value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSetList(String key, List value ) {
        try {
            redisTemplate.opsForList().rightPushAll(getXmKey(key), value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(getXmKey(key), value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     * @param key 键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index,Object value) {
        try {
            redisTemplate.opsForList().set(getXmKey(key), index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }



    public long lRemoveOne(String key, Object value) {
        try {
            Long remove = lRemove(key,1,value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 移除N个值为value
     * @param key 键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key,long count,Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(getXmKey(key), count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(String key,long time){
        try {
            if(time>0){
                redisTemplate.expire(getXmKey(key), time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 删除 第一个值
     * @param key 键
     * @return
     */
    public boolean delListOne(String key){

        Object o = redisTemplate.opsForList().leftPop(getXmKey(key));
        if(o!=null) return true;
        return false;

    }


    /**
     * 获取可以的过期时间
     * @param key 键
     * @return
     */
    public Long getExpireTime(String key){

        try {
            return  redisTemplate.getExpire(getXmKey(key));
        } catch (Exception e) {
            e.printStackTrace();
            return null ;
        }

    }



    /**
     * 获取前缀相同的key
     * @param key 键
     * @return
     */
    public List getListByKey(String key){

        try {

            String prefix =  getXmKey(key) + "*";
            // 获取所有的key
            Set<String> keys = redisTemplate.keys(prefix);
            List<String> keysList = keys.stream().collect(Collectors.toList());
            return  keysList;
        } catch (Exception e) {
            e.printStackTrace();
            return null ;
        }

    }


    //================================Map=================================
    /**
     * HashGet
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key,String item){
        return redisTemplate.opsForHash().get(getXmKey(key), item);
    }

    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object,Object> hmget(String key){
        return redisTemplate.opsForHash().entries(getXmKey(key));
    }

    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String,Object> map){
        try {
            redisTemplate.opsForHash().putAll(getXmKey(key), map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     * @param key 键
     * @param map 对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String,Object> map, long time){
        try {
            redisTemplate.opsForHash().putAll(getXmKey(key), map);
            if(time>0){
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key,String item,Object value) {
        try {
            redisTemplate.opsForHash().put(getXmKey(key), item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @param time 时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key,String item,Object value,long time) {
        try {
            redisTemplate.opsForHash().put(getXmKey(key), item, value);
            if(time>0){
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     * @param key 键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item){
        redisTemplate.opsForHash().delete(getXmKey(key),item);
    }

    /**
     * 判断hash表中是否有该项的值
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item){
        return redisTemplate.opsForHash().hasKey(getXmKey(key), item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * @param key 键
     * @param item 项
     * @param by 要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item,double by){
        return redisTemplate.opsForHash().increment(getXmKey(key), item, by);
    }

    /**
     * hash递减
     * @param key 键
     * @param item 项
     * @param by 要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item,double by){
        return redisTemplate.opsForHash().increment(getXmKey(key), item,-by);
    }



}
